package com.leyou.search.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.leyou.common.utils.JsonUtils;
import com.leyou.item.bo.SpuBo;
import com.leyou.item.pojo.*;
import com.leyou.search.client.BrandClient;
import com.leyou.search.client.CategoryClient;
import com.leyou.search.client.GoodsClient;
import com.leyou.search.client.SpecificationClient;
import com.leyou.search.pojo.Goods;
import com.leyou.search.pojo.SearchRequest;
import com.leyou.search.pojo.SearchResult;
import com.leyou.search.repository.GoodsRepository;
import com.leyou.search.utils.ChooseSegmentUtil;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class SearchService {

    @Autowired
    private CategoryClient categoryClient;

    @Autowired
    private GoodsClient goodsClient;

    @Autowired
    private SpecificationClient specificationClient;

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private BrandClient brandClient;

    //进行搜索,使用新的返回值SearchResult进行改造
    public SearchResult search(SearchRequest searchRequest) {
        //搜索的关键字
        String key = searchRequest.getKey();
        if (StringUtils.isBlank(key)) {
            // 如果用户没搜索条件，我们可以给默认的，或者返回null
            return null;
        }

        //创建查询构建器
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
      /*  //设置基本查询条件，查询“all”字段
        queryBuilder.withQuery(QueryBuilders.matchQuery("all", key));*/

        //改造基本查询，添加过滤条件
        QueryBuilder query = buildBasicQueryWithFilter(searchRequest);
        queryBuilder.withQuery(query);

        //设置查询条件结果过滤，第一个参数是需要的结果，第二个参数是不需要的结果，两个参数的设置不能冲突
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id", "skus", "subTitle"}, null));

        //设置分页信息
        Integer page = searchRequest.getPage() - 1;//elasticsearch的分页是从第0页开始的
        Integer size = searchRequest.getSize(); //固定大小 10
        queryBuilder.withPageable(PageRequest.of(page, size));

        //设置排序信息
        String sortBy = searchRequest.getSortBy();
        Boolean descending = searchRequest.getDescending();
        if (StringUtils.isNotBlank(sortBy)) {
            //若排序字段不为空，则根据descending的值进行排序
            queryBuilder.withSort(SortBuilders.fieldSort(sortBy).order(descending ? SortOrder.DESC : SortOrder.ASC));
        }

        //设置聚合信息
        String categoryAggName = "categoryAgg"; //商品分类的聚合名称
        String brandAggName = "brandAgg";  //品牌的聚合名称
        //对商品分类进行聚合
        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAggName).field("cid3"));
        //对品牌进行聚合
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAggName).field("brandId"));

        //开始查询并返回结果 ，用AggregatedPage进行接收，才能处理聚合结果
        AggregatedPage<Goods> goodsPage = (AggregatedPage<Goods>) goodsRepository.search(queryBuilder.build());

        //对结果进行解析
        long total = goodsPage.getTotalElements();//总条数
        long totalPage = Long.valueOf((total + size - 1) / size); //总页数
        //解析商品分类的聚合结果
        List<Category> categories = this.getCategoryAgg(goodsPage.getAggregation(categoryAggName));
        //解析品牌的聚合结果
        List<Brand> brands = this.getBrandAgg(goodsPage.getAggregation(brandAggName));

        //对规格参数进行聚合
        List<Map<String, Object>> specs = null;
        //先判断商品分类的数量是否为1，只有分类是唯一的，规格参数才有显示的意义和必要
        if (categories.size() == 1) {
            //当商品分类数量是1时，调用方法，对规格参数进行聚合，方法参数是分类id
            //将前面构造的基本查询传过去，根据查询结果进行精准聚合
            specs = getSpecsAgg(categories.get(0).getId(),query);
        }

        return new SearchResult(total, totalPage, goodsPage.getContent(), categories, brands, specs);
    }

    //构建有过滤条件的基本查询
    private QueryBuilder buildBasicQueryWithFilter(SearchRequest searchRequest) {

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //基本查询条件
        boolQueryBuilder.must(QueryBuilders.matchQuery("all",searchRequest.getKey()).operator(Operator.AND));
        //过滤条件构建器
        BoolQueryBuilder filterQueryBuilder = QueryBuilders.boolQuery();
        //整理过滤条件
        Map<String, String> filter = searchRequest.getFilter();
        Set<Map.Entry<String, String>> entries = filter.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            String key = entry.getKey();
            String value = entry.getValue();
            //商品分类和品牌已经聚合
            if (key != "cid3" && key != "brandId") {
                key = "specs." + key + ".keyword";
            }
            //字符串类型，进行term查询
            filterQueryBuilder.must(QueryBuilders.termQuery(key,value));
        }
        //添加过滤条件
        boolQueryBuilder.filter(filterQueryBuilder);
        return boolQueryBuilder;
    }

    //对商品的规格参数进行聚合操作  同时把查询条件传过来，即根据查询的结果进行聚合
    private List<Map<String, Object>> getSpecsAgg(Long cid,QueryBuilder query) {
        //先根据分类id查询可被搜索的规格参数数据
        List<SpecParam> specParams = this.specificationClient.querySpecParam(null, cid, true, null);

        //创建集合，保存规格参数过滤条件
        List<Map<String, Object>> specs = new ArrayList<>();

        //对规格参数进行聚合
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //先根据基本查询条件进行查询 然后聚合
        queryBuilder.withQuery(query);

        for (SpecParam specParam : specParams) {
            String specName = specParam.getName();//每个规格参数的聚合名称
            //添加聚合条件 聚合字段为“specs + name + .keyword”
            queryBuilder.addAggregation(AggregationBuilders.terms(specName).field("specs." + specName + ".keyword"));
        }
        //查询得到聚合结果,用AggregatedPage接收才能处理聚合结果
        AggregatedPage<Goods> search = (AggregatedPage<Goods>) this.goodsRepository.search(queryBuilder.build());
        //获取所有的聚合结果,存入map集合中，key为每个聚合的名称
        Map<String, Aggregation> aggregationMap = search.getAggregations().asMap();
        //解析聚合结果
        for (SpecParam specParam : specParams) {
            //定义一个map集合，用来存储规格参数的名称和对应的搜索字段的值
            Map<String, Object> spec = new HashMap<>();
            spec.put("k", specParam.getName());
            //取出每个聚合中的桶 因为是String类型，所以用StringTerms进行接收
            StringTerms terms = (StringTerms) aggregationMap.get(specParam.getName());
            List<StringTerms.Bucket> buckets = terms.getBuckets();
            //获取桶中所有的key，声明一个集合,来保存key
            List<String> list = new ArrayList<>();
            for (StringTerms.Bucket bucket : buckets) {
                String key = bucket.getKeyAsString();
                if (!key.equals("其他")) {
                    if (!key.equals("其它")) {
                        list.add(key);
                    }
                }
            }
            list.add("其他");
            spec.put("options", list);
            //添加每个规格参数到规格参数组中
            specs.add(spec);
        }
        return specs;
    }


    //解析商品分类的聚合结果
    private List<Category> getCategoryAgg(Aggregation aggregation) {
        List<Category> categories = null;
        try {
            //创建集合来接收分类处理结果
            categories = new ArrayList<>();
            //商品分类聚合 类型是Long型，故用LongTerms接收
            LongTerms categoryAgg = (LongTerms) aggregation;
            //取出聚合中的桶
            List<LongTerms.Bucket> buckets = categoryAgg.getBuckets();
            //声明一个集合，来保存桶中key的值
            List<Long> cids = new ArrayList<>();
            for (LongTerms.Bucket bucket : buckets) {
                cids.add(bucket.getKeyAsNumber().longValue());
            }
            //调用方法，查询cid对应的分类名称
            List<String> names = this.categoryClient.queryNameByIds(cids);
            //将分类对象存入之前定义好的集合中
            for (int i = 0; i < names.size(); i++) {
                Category category = new Category();
                category.setId(cids.get(i));
                category.setName(names.get(i));
                categories.add(category);
            }
            return categories;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    //解析品牌的聚合结果
    private List<Brand> getBrandAgg(Aggregation aggregation) {
        try {
            //品牌聚合  类型也是Long型 故用LongTerms接收
            LongTerms brandAgg = (LongTerms) aggregation;
            //取出聚合中的桶
            List<LongTerms.Bucket> buckets = brandAgg.getBuckets();
            //声明一个集合 ，来保存桶中的key
            List<Long> brandIds = new ArrayList<>();
            for (LongTerms.Bucket bucket : buckets) {
                brandIds.add(bucket.getKeyAsNumber().longValue());
            }
            //调用方法，查询brandId对应的品牌
            return this.brandClient.queryBrandByIds(brandIds);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /*//进行搜索
    public PageResult<Goods> search(SearchRequest searchRequest) {
        //搜索的关键字
        String key = searchRequest.getKey();
        if (StringUtils.isBlank(key)) {
            // 如果用户没搜索条件，我们可以给默认的，或者返回null
            return null;
        }
        Integer page = searchRequest.getPage() - 1;//elasticsearch的分页是从第0页开始的
        Integer size = searchRequest.getSize(); //固定大小 20

        //创建查询构建器
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //设置查询条件结果过滤，第一个参数是需要的结果，第二个参数是不需要的结果，两个参数的设置不能冲突
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id","skus","subTitle"},null));
        //设置查询条件，查询“all”字段
        queryBuilder.withQuery(QueryBuilders.matchQuery("all",key));
        //设置分页信息
        queryBuilder.withPageable(PageRequest.of(page,size));

        //设置排序信息
        String sortBy = searchRequest.getSortBy();
        System.out.println(sortBy);
        Boolean descending = searchRequest.getDescending();
        if (StringUtils.isNotBlank(sortBy)) {
            //若排序字段不为空，则根据descending的值进行排序
            queryBuilder.withSort(SortBuilders.fieldSort(sortBy).order(descending?SortOrder.DESC:SortOrder.ASC));
        }

        //开始查询并返回结果
        Page<Goods> goodsPage = goodsRepository.search(queryBuilder.build());
        //对结果进行解析
        long total = goodsPage.getTotalElements();//总条数
        System.out.println(goodsPage.getTotalPages());
        long totalPage = (total + size - 1) / size; //总页数
        return new PageResult<>(total, totalPage, goodsPage.getContent());
    }*/
}
